package com.naiterui.ehp.bs.cms.modules.wms.service.impl;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.naiterui.ehp.bp.bo.b2c.SkuUsageBO;
import com.naiterui.ehp.bp.bo.medication.MedicationDefaultUsageBO;
import com.naiterui.ehp.bp.support.exception.BusinessException;
import com.naiterui.ehp.bp.support.utils.JsonMapper;
import com.naiterui.ehp.bp.utils.beans.BeanUtil;
import com.naiterui.ehp.bs.cms.common.exception.BusinessExceptionCode;
import com.naiterui.ehp.bs.cms.common.vo.PageParamsVO;
import com.naiterui.ehp.bs.cms.common.vo.PageVO;
import com.naiterui.ehp.bs.cms.modules.sys.utils.DictUtil;
import com.naiterui.ehp.bs.cms.modules.sys.vo.DictVO;
import com.naiterui.ehp.bs.cms.modules.wms.entity.Sku;
import com.naiterui.ehp.bs.cms.modules.wms.feign.IB2cFeignClient;
import com.naiterui.ehp.bs.cms.modules.wms.mapper.MedicineDefaultUsageMapper;
import com.naiterui.ehp.bs.cms.modules.wms.service.IMedicineDefaultUsageService;
import com.naiterui.ehp.bs.cms.modules.wms.vo.MedicineDefaultUsageDetailVO;
import com.naiterui.ehp.bs.cms.modules.wms.vo.MedicineDefaultUsageVO;
import com.naiterui.ehp.bs.cms.modules.wms.vo.MedicineUsageExcelVO;
import com.naiterui.ehp.bs.cms.modules.wms.vo.MedicineUsageResultVO;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * @author yangfuya
 * @since 2021/11/10
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MedicineDefaultUsageServiceImpl extends ServiceImpl<MedicineDefaultUsageMapper, Sku>
    implements IMedicineDefaultUsageService {

    private final IB2cFeignClient ib2cFeignClient;
    @Value("${ehp.domain.img}")
    private String imgHost;
    @Value("${file.base.prefix}")
    private String fileBasePrefix;
    @Value("${ehp.medicine-usage-temp-file-path}")
    private String tempFilePath;
    @Value("${ehp.medicine-usage-expert-model-path}")
    private String expertModelPath;

    @Override
    public PageVO<MedicineDefaultUsageVO> queryPage(PageParamsVO pageParamsVO,
        MedicineDefaultUsageVO medicineDefaultUsageVO) {
        QueryWrapper<MedicineDefaultUsageVO> entityWrapper =
            this.buildEntityWrapper(pageParamsVO, medicineDefaultUsageVO);
        IPage<MedicineDefaultUsageVO> page = this.baseMapper.getPage(pageParamsVO.getPage(), entityWrapper);
        return PageVO.toPageVo(page);
    }

    @Override
    public MedicineDefaultUsageDetailVO getById(Long id) throws BusinessException {
        MedicineDefaultUsageVO usageVO = this.baseMapper.getById(id);
        MedicationDefaultUsageBO defaultUsageBO = ib2cFeignClient.getBySkuId(id);
        MedicineDefaultUsageDetailVO medicineDefaultUsageDetailVO = new MedicineDefaultUsageDetailVO();
        medicineDefaultUsageDetailVO.setMedicineInfo(usageVO);
        medicineDefaultUsageDetailVO.setMedicationDefaultUsage(defaultUsageBO);
        return medicineDefaultUsageDetailVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void edit(MedicationDefaultUsageBO medicationDefaultUsageBO) throws BusinessException {
        // 范围检查
        this.checkParams(medicationDefaultUsageBO);
        log.info("编辑药品配置: {}", JsonMapper.toJson(medicationDefaultUsageBO));
        ib2cFeignClient.insertOrUpdate(medicationDefaultUsageBO);
        Long skuId = medicationDefaultUsageBO.getSkuId();
        SkuUsageBO skuUsageBO = new SkuUsageBO();
        skuUsageBO.setId(skuId);
        Integer dosageCycle = medicationDefaultUsageBO.getDosageCycle();
        String dosageCycleUnit = medicationDefaultUsageBO.getDosageCycleUnit();
        Integer dosageCount = medicationDefaultUsageBO.getDosageCount();
        String eachDosageCount = medicationDefaultUsageBO.getEachDosageCount();
        String eachDoseUnit = medicationDefaultUsageBO.getEachDoseUnit();
        // 默认用法用量已配置
        if (dosageCycle != null && StrUtil.isNotBlank(dosageCycleUnit) && dosageCount != null
            && StrUtil.isNotBlank(eachDosageCount) && StrUtil.isNotBlank(eachDoseUnit)
            && StrUtil.isNotBlank(medicationDefaultUsageBO.getUsageMethod())) {
            skuUsageBO.setDefaultUsage(1);
        }
        // 默认用法用量未配置
        else {
            skuUsageBO.setDefaultUsage(0);
        }
        // 安全用量已配置
        if (medicationDefaultUsageBO.getFrequencyMax() != null && medicationDefaultUsageBO.getFrequencyMin() != null
            && medicationDefaultUsageBO.getDoseMax() != null && medicationDefaultUsageBO.getDoseMin() != null
            && medicationDefaultUsageBO.getMedicationCycleMax() != null) {
            skuUsageBO.setSafeUsage(1);
        }
        // 安全用量未配置
        else {
            skuUsageBO.setSafeUsage(0);
        }
        // 拼接药品用法用量
        if (dosageCycle != null && StrUtil.isNotBlank(dosageCycleUnit) && dosageCount != null && eachDosageCount != null
            && StrUtil.isNotBlank(eachDoseUnit)) {
            skuUsageBO.setUsageDosage((dosageCycle == 1 ? "每" : dosageCycle) + dosageCycleUnit + dosageCount + "次，每次"
                + eachDosageCount + eachDoseUnit);
        }
        if (skuUsageBO.getDefaultUsage() != null || skuUsageBO.getSafeUsage() != null) {
            skuUsageBO.setLastConfigTime(new Date());
            log.info("修改sku用法用量和安全用量: {}", JsonMapper.toJson(skuUsageBO));
            ib2cFeignClient.updateSkuUsage(skuUsageBO);
        }
    }

    @Override
    public MedicineUsageResultVO importExpert(MultipartFile file) throws BusinessException {
        ImportParams params = new ImportParams();
        params.setTitleRows(2);
        params.setStartRows(0);
        List<MedicineUsageExcelVO> list;
        try {
            list = ExcelImportUtil.importExcel(file.getInputStream(), MedicineUsageExcelVO.class, params);
        } catch (Exception e) {
            throw new BusinessException(BusinessExceptionCode.FILE_IMPORT_ERROR, "文件上传失败， 检查文件格式是否正确");
        }
        MedicationDefaultUsageBO medicationDefaultUsageBO;
        // 所有skuId集合
        List<Long> skuIdAll = this.baseMapper.getSkuIdAll();
        // 默认频次单位
        List<String> dosageCycleUnitList = getDictList("default-dosage-cycle-unit");
        // 服用方式
        List<String> usageMethodList = getDictList("usage-method");
        // 服用单位
        List<String> packingUnitLimitList = getDictList("product_packing_unit_limit");
        // 成功条数
        int num = 0;
        StringBuffer sb;
        // 导入数据并生成结果
        for (MedicineUsageExcelVO vo : list) {
            try {
                sb = new StringBuffer();
                if (vo.getSkuId() == null) {
                    vo.setResult("导入失败，未填写SKU ID");
                    continue;
                }
                // 是否是有效的skuId
                if (skuIdAll.stream().noneMatch(s -> s.equals(vo.getSkuId()))) {
                    vo.setResult("导入失败，SKU ID 无效");
                    continue;
                }
                // 是否是有效的默认频次单位
                if (vo.getDosageCycleUnit() != null
                    && dosageCycleUnitList.stream().noneMatch(s -> s.equals(vo.getDosageCycleUnit()))) {
                    sb.append("默认频次单位无效,");
                }
                // 是否是有效的服用方式
                if (vo.getUsageMethod() != null
                    && usageMethodList.stream().noneMatch(s -> s.equals(vo.getUsageMethod()))) {
                    sb.append("服用方式无效,");
                }
                // 是否是有效的药品服用单位
                if (vo.getEachDoseUnit() != null
                    && packingUnitLimitList.stream().noneMatch(s -> s.equals(vo.getEachDoseUnit()))) {
                    sb.append("药品服用单位无效,");
                }
                // 是否超出数值范围
                if (isAllNotScopeInt(vo.getDosageCycle())) {
                    sb.append("默认频次周期数小数点过长或者超出数值有效范围,");
                }
                if (isAllNotScopeInt(vo.getDosageCount())) {
                    sb.append("默认频次数小数点过长或者超出数值有效范围,");
                }
                if (isAllNotScopeInt(vo.getFrequencyMax(), vo.getFrequencyMin())) {
                    sb.append("频次小数点过长或者超出数值有效范围,");
                }
                if (isAllNotScope(vo.getDoseMax(), vo.getDoseMin())) {
                    sb.append("剂量小数点过长或者超出数值有效范围,");
                }
                if (isAllNotScopeStr(vo.getEachDosageCount())) {
                    sb.append("默认剂量小数点过长或者超出数值有效范围,");
                }
                // 最小值是否大于最大值或者相等
                if (checkIsGe(vo.getFrequencyMin(), vo.getFrequencyMax())) {
                    sb.append("频次最小值大于最大值或相等,");
                }
                if (checkIsGeD(vo.getDoseMin(), vo.getDoseMax())) {
                    sb.append("剂量最小值大于最大值或相等,");
                }
                medicationDefaultUsageBO = new MedicationDefaultUsageBO();
                BeanUtil.copyProperties(vo, medicationDefaultUsageBO);
                this.edit(medicationDefaultUsageBO);
                vo.setResult(
                    StrUtil.isBlank(sb) ? "导入成功" : sb.toString().substring(0, sb.toString().length() - 1));
                num++;
            } catch (Exception e) {
                vo.setResult("导入失败，请按照模板指示进行填写");
                log.error("药品配置导入异常：vo={}, errmsg={}", JsonMapper.toJson(vo), e.getMessage());
            }
        }
        return resultExcel(list, num);
    }

    @Override
    public String modelExpert() {
        return imgHost + expertModelPath;
    }

    private QueryWrapper<MedicineDefaultUsageVO> buildEntityWrapper(PageParamsVO pageParamsVO,
        MedicineDefaultUsageVO medicineDefaultUsageVO) {
        QueryWrapper<MedicineDefaultUsageVO> entityWrapper = pageParamsVO.getEntityWrapper("t");
        // 商品id
        Long id = medicineDefaultUsageVO.getId();
        entityWrapper.eq(id != null, "t.id", id);

        // 商品名称
        String name = medicineDefaultUsageVO.getName();
        entityWrapper.like(StringUtils.isNotBlank(name), "pp.name", name);

        // 商品通用名
        String commonName = medicineDefaultUsageVO.getCommonName();
        entityWrapper.like(StringUtils.isNotBlank(commonName), "pp.common_name", commonName);

        // 默认用法用量
        Integer defaultUsage = medicineDefaultUsageVO.getDefaultUsage();
        entityWrapper.eq(defaultUsage != null, "t.default_usage", defaultUsage);

        // 默认用法用量
        Integer safeUsage = medicineDefaultUsageVO.getSafeUsage();
        entityWrapper.eq(safeUsage != null, "t.safe_usage", safeUsage);

        // 安全分类
        Integer nmpaType = medicineDefaultUsageVO.getNmpaType();
        entityWrapper.eq(nmpaType != null, "pp.nmpa_type", nmpaType);

        // 创建时间
        String startTime = medicineDefaultUsageVO.getStartTime();
        String endTime = medicineDefaultUsageVO.getEndTime();
        entityWrapper.ge(StrUtil.isNotBlank(startTime), "t.last_config_time", startTime + " 00:00:00");
        entityWrapper.le(StrUtil.isNotBlank(endTime), "t.last_config_time", endTime + " 23:59:59");
        return entityWrapper;
    }

    /**
     * 药品配置导入结果返回
     * 
     * @param list
     *            表格数据
     * @param num
     *            成功条数
     * @return 结果excel
     * @throws BusinessException
     *             业务异常
     */
    private MedicineUsageResultVO resultExcel(List<MedicineUsageExcelVO> list, int num) throws BusinessException {
        String resultPath = tempFilePath + "result_" + System.currentTimeMillis() + ".xls";
        log.info("药品配置导入结果临时文件地址：resultPath={}", fileBasePrefix + resultPath);
        File resultFile = new File("/" + fileBasePrefix + resultPath);
        try {
            FileUtils.forceMkdirParent(resultFile);
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), MedicineUsageExcelVO.class, list);
            workbook.write(byteArrayOutputStream);
            FileUtils.writeByteArrayToFile(resultFile, byteArrayOutputStream.toByteArray());
        } catch (IOException e) {
            throw new BusinessException(BusinessExceptionCode.FILE_IMPORT_ERROR, "导入结果文件写入数据异常");
        }
        MedicineUsageResultVO medicineUsageResultVO = new MedicineUsageResultVO();
        medicineUsageResultVO.setUrl(imgHost + resultPath);
        medicineUsageResultVO.setNum(num);
        return medicineUsageResultVO;
    }

    /**
     * 检查参数范围并处理
     * 
     * @param medicationDefaultUsageBO
     *            用法用量和安全用量
     */
    private void checkParams(MedicationDefaultUsageBO medicationDefaultUsageBO) {
        if (medicationDefaultUsageBO.getDosageCycle() != null
            && isAllNotScopeInt(medicationDefaultUsageBO.getDosageCycle())) {
            medicationDefaultUsageBO.setDosageCycle(null);
        }
        if (medicationDefaultUsageBO.getDosageCount() != null
            && isAllNotScopeInt(medicationDefaultUsageBO.getDosageCount())) {
            medicationDefaultUsageBO.setDosageCount(null);
        }
        if (medicationDefaultUsageBO.getFrequencyMax() != null
            && isAllNotScopeInt(medicationDefaultUsageBO.getFrequencyMax())) {
            medicationDefaultUsageBO.setFrequencyMax(null);
        }
        if (medicationDefaultUsageBO.getFrequencyMin() != null
            && isAllNotScopeInt(medicationDefaultUsageBO.getFrequencyMin())) {
            medicationDefaultUsageBO.setFrequencyMin(null);
        }
        if (medicationDefaultUsageBO.getMedicationCycleMax() != null
            && isAllNotScopeInt(medicationDefaultUsageBO.getMedicationCycleMax())) {
            medicationDefaultUsageBO.setMedicationCycleMax(null);
        }
        if (medicationDefaultUsageBO.getDoseMax() != null && isAllNotScope(medicationDefaultUsageBO.getDoseMax())) {
            medicationDefaultUsageBO.setDoseMax(null);
        }
        if (medicationDefaultUsageBO.getDoseMin() != null && isAllNotScope(medicationDefaultUsageBO.getDoseMin())) {
            medicationDefaultUsageBO.setDoseMin(null);
        }
        if (StrUtil.isNotBlank(medicationDefaultUsageBO.getEachDosageCount())
            && isAllNotScopeStr(medicationDefaultUsageBO.getEachDosageCount())) {
            medicationDefaultUsageBO.setEachDosageCount(null);
        }
    }

    /**
     * 是否不在范围内
     *
     * @param ds
     *            多个double参数
     * @return true/false
     */
    private boolean isAllNotScope(Double... ds) {
        if (ArrayUtil.isEmpty(ds)) {
            return false;
        }
        for (Double d : ds) {
            if (d == null) {
                continue;
            }
            if (d <= 0) {
                return true;
            }
            if (checkIsDoublePointTwo(d) || d > MedicineUsageExcelVO.max || d < MedicineUsageExcelVO.dmin) {
                return true;
            }
        }
        return false;
    }

    /**
     * 是否不在范围内
     * 
     * @param is
     *            多个int参数
     * @return true/false
     */
    private boolean isAllNotScopeInt(Integer... is) {
        if (ArrayUtil.isEmpty(is)) {
            return false;
        }
        for (Integer i : is) {
            if (i == null) {
                continue;
            }
            if (i <= 0 || i > MedicineUsageExcelVO.max) {
                return true;
            }
        }
        return false;
    }

    /**
     * 是否不在范围内
     *
     * @param strs
     *            多个字符串参数
     * @return true/false
     */
    private boolean isAllNotScopeStr(String... strs) {
        if (ArrayUtil.isEmpty(strs)) {
            return false;
        }
        for (String s : strs) {
            if (StrUtil.isBlank(s)) {
                continue;
            }
            double d = Double.parseDouble(s);
            BigDecimal b = new BigDecimal(d);
            d = b.doubleValue();
            if (d <= 0) {
                return true;
            }
            if (checkIsDoublePointTwo(d) || d > MedicineUsageExcelVO.max || d < MedicineUsageExcelVO.dmin) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断double是否是两位小数点
     * 
     * @param param
     *            参数
     * @return true/false
     */
    public static boolean checkIsDoublePointTwo(double param) {
        BigDecimal bd = new BigDecimal(String.valueOf(param));
        String[] ss = bd.toString().split("\\.");
        if (ss.length <= 1) {
            return false;
        }
        return ss[1].length() > 2;
    }

    private List<String> getDictList(String type) {
        List<DictVO> list = DictUtil.queryDict(type);
        return list.stream().map(DictVO::getValue).collect(Collectors.toList());
    }

    /**
     * 检查最小值是否大于最大值或者相等
     *
     * @param min
     *            最小值
     * @param max
     *            最大值
     * @return 结果
     */
    private boolean checkIsGeD(Double min, Double max) {
        if (min == null || max == null) {
            return false;
        }
        return min > max || min.equals(max);
    }

    /**
     * 检查最小值是否大于最大值或者相等
     * 
     * @param min
     *            最小值
     * @param max
     *            最大值
     * @return 结果
     */
    private boolean checkIsGe(Integer min, Integer max) {
        if (min == null || max == null) {
            return false;
        }
        return min > max || min.equals(max);
    }
}
